#! /usr/bin/env python
# -*- coding: utf-8 -*-

"""
This Module is for defining the sequenses of a commands to plot the fracture 
orthodiagramm. The Module reads a list of a fracture data and returns  the 
sequenses of a commands. The fracture data is a list of the numbers and chars.
Commands are independent from the method of the plotting and from 
the coordinate system. And it is easy to addapt any other part of the programm 
to work with this.
There are several commands:
Point (X1, Y1), size, color
Line (X1, Y1, X2, Y2), width, color
Upper left corner of t he canvashas the cordinate (-1,-1). Bottom Right corner 
has the coordinates (1,1). the center of the canvas has the coordinates (0,0).
Each line in list must has at least two numbers: Strike and Dip. 
For example: [125, 15]
Such numbers definde the attitude of a plane. 
the next word  is for definding the color.

The complete example:
[125, 15, 'red']

"""
from plane import Plane
from math import sin, cos, radians, sqrt, atan2



def ReturnCommands(data, isnormals=False, isnormalshard=False, allTR=False):
    Commands = []
    plot = plot_func()    
    for plane_att in data:               
        if isnormals:
            if not plane_att[4]:
                plane_att[4]='n'
        if isnormalshard:
            if not plane_att[4]:
                plane_att[4]='n'
            plane_att[4] = plane_att[4].replace('p', 'n')
            plane_att[4] = plane_att[4].replace('l', 'n')
            plane_att[4] = plane_att[4].replace('w', 'r')
            plane_att[4] = plane_att[4].replace('q', 't')
        if not plane_att[4]: plane_att[4] = 'p'
        if plane_att[2]:
            if plane_att[4].find('q') == -1 and \
               plane_att[4].find('w') == -1 and \
               plane_att[4].find('t') == -1 and \
               plane_att[4].find('r') == -1:
                if isnormals or isnormalshard:
                    plane_att[4]+='r'
                else:
                    plane_att[4]+='w'
        if allTR and plane_att[2]:
            plane_att[4] = plane_att[4].replace('q', 'w')
            plane_att[4] = plane_att[4].replace('t', 'r')
        for f in plot.func_list:            
            if plane_att[4].find(f)>-1:
                func = getattr(plot, f)                
                Commands.append(func(plane_att))
    return Commands
        


class  plot_func():
    '''
    Class for plotting different types of data on the stereographic diagrams.
    Each metod servies for plottin its own type of data (great circle,normal 
    to a plane, etc).
    '''
    func_list = ['p', 'n', 'l', 'q', 'w', 'r', 't']    
    
    def p(self, p_att):        
        a = Plane(p_att[0], p_att[1])
        cmds = []
        cmds.append(['point', 
                     self.get_coords(a), 
                     p_att[6]])
        cmds.append(['point', 
                     self.get_coords(a, "upper"), 
                     p_att[6]])
        return cmds

    def n(self, p_att):        
        a = Plane(p_att[0], p_att[1]+90)
        cmds = []
        cmds.append(['point', 
                     self.get_coords(a), 
                     p_att[6]])
        cmds.append(['point', 
                     self.get_coords(a, "upper"), 
                     p_att[6]])
        return cmds
    
    def l(self, p_att):
        a = Plane(p_att[0], p_att[1]+90)
        cmds = []
        if p_att[1]==90:
            x1,y1=self.get_coords(a.rotated(90))
            x2,y2=self.get_coords(a.rotated(-90))
            xv1,yv1=self.get_coords(a.rotated(90),'upper')
            xv2,yv2=self.get_coords(a.rotated(-90), 'upper')
            cmds.append(['line', 
                        [x1,y1,x2,y2], 
                        p_att[6]])
            cmds.append(['line', 
                        [xv1,yv1,xv2,yv2], 
                        p_att[6]])
            return cmds
        points = []
        for angle in range(-89, 89, 5):
            x,y = self.get_coords(a.rotated(angle))
            points.append([x,y])
        for i in range(len(points)-1):
            x1, y1 = points[i][0], points[i][1]
            x2, y2 = points[i+1][0], points[i+1][1]
            if sqrt((x1-x2)**2 + (y1-y2)**2) > 0.51 : continue
            cmds.append(['line', 
                      [points[i][0],points[i][1],points[i+1][0],points[i+1][1]], 
                         p_att[6]])
        for angle in range(-89, 89, 1):
            x,y = self.get_coords(a.rotated(angle),'upper')
            points.append([x,y])
        for i in range(len(points)-1):
            x1, y1 = points[i][0], points[i][1]
            x2, y2 = points[i+1][0], points[i+1][1]
            if sqrt((x1-x2)**2 + (y1-y2)**2) > 0.6 : continue
            cmds.append(['line', 
                      [points[i][0],points[i][1],points[i+1][0],points[i+1][1]], 
                         p_att[6]])
        return cmds
    
    def q(self, p_att):
        a = Plane(p_att[0], p_att[1])
        if p_att[2]==0:p_att[2]=.0001
        if p_att[2]==180:p_att[2]=180.0001
        direction = p_att[2]
        if (a>90 and a<180) or (a<360 and a>270):
            direction = (direction+180)%360
        b = Plane(direction, .0001)
        cmds = []
        
        al = 14 # length of an arrow in degrees
        c = a.get_perpendicular_between(b).normal()
        ca = c.return_angle_between(a)
        ang_sub = c.dir - a.dir
        points=[]
        for angle in range (ca, ca + al, 5):
            if (ang_sub>0 and ang_sub<180) or \
               (ang_sub<-180 and ang_sub>-360):
                a = c.rotated(-1*angle)
            else:
                a = c.rotated(angle)
            if a.dip <0: a = Plane(a.dir+180, a.dip*-1)
            x,y = self.get_coords(a)
            points.append([x,y])
        for i in range(len(points)-1):
            cmds.append([   'line', 
                            [ points[i][0], points[i][1], 
                              points[i+1][0], points[i+1][1] ],
                            p_att[6]])
        b = Plane(p_att[2]+180, 0)
        a = Plane(p_att[0], p_att[1])
        c = a.get_perpendicular_between(b).normal()
        ca = c.return_angle_between(a)
        ang_sub = c.dir - a.dir
        points=[]
        for angle in range (ca, ca + al, 5):
            if (ang_sub>0 and ang_sub<180) or \
               (ang_sub<-180 and ang_sub>-360):
                a = c.rotated(-1*angle)
            else:
                a = c.rotated(angle)
            if a.dip <0: a = Plane(a.dir+180, a.dip*-1)
            x,y = self.get_coords(a)
            points.append([x,y])
        for i in range(len(points)-1):
            cmds.append([   'line', 
                            [ points[i][0], points[i][1], 
                              points[i+1][0], points[i+1][1] ],
                            p_att[6]])
        ### processing the same 'q' for the upper hemisphere
        a = Plane(p_att[0], p_att[1])
        if p_att[2]==0:p_att[2]=.0001
        if p_att[2]==180:p_att[2]=180.0001
        direction = p_att[2]
        if (a>90 and a<180) or (a<360 and a>270):
            direction = (direction+180)%360
        b = Plane(direction, .0001)
        
        al = 14 # length of an arrow in degrees
        c = a.get_perpendicular_between(b).normal()
        ca = c.return_angle_between(a)
        ang_sub = c.dir - a.dir
        points=[]
        for angle in range (ca, ca + al, 5):
            if (ang_sub>0 and ang_sub<180) or \
               (ang_sub<-180 and ang_sub>-360):
                a = c.rotated(-1*angle)
            else:
                a = c.rotated(angle)
            if a.dip <0: a = Plane(a.dir+180, a.dip*-1)
            x,y = self.get_coords(a, 'upper')
            points.append([x,y])
        for i in range(len(points)-1):
            cmds.append([   'line', 
                            [ points[i][0], points[i][1], 
                              points[i+1][0], points[i+1][1] ],
                            p_att[6]])
        b = Plane(p_att[2]+180, 0)
        a = Plane(p_att[0], p_att[1])
        c = a.get_perpendicular_between(b).normal()
        ca = c.return_angle_between(a)
        ang_sub = c.dir - a.dir
        points=[]
        for angle in range (ca, ca + al, 5):
            if (ang_sub>0 and ang_sub<180) or \
               (ang_sub<-180 and ang_sub>-360):
                a = c.rotated(-1*angle)
            else:
                a = c.rotated(angle)
            if a.dip <0: a = Plane(a.dir+180, a.dip*-1)
            x,y = self.get_coords(a, 'upper')
            points.append([x,y])
        for i in range(len(points)-1):
            cmds.append([   'line', 
                            [ points[i][0], points[i][1], 
                              points[i+1][0], points[i+1][1] ],
                            p_att[6]])
        return cmds

    def w(self, p_att):
        a = Plane(p_att[0], p_att[1])
        if p_att[2]==0:p_att[2]=.0001
        if p_att[2]==180:p_att[2]=180.0001
        direction = p_att[2]
        if (a>90 and a<180) or (a<360 and a>270):
            direction = (direction+180)%360
        if (direction - a.dir)%180 < 90: direction = (direction + 180) % 360
        b = Plane(direction, .0001)
        cmds = []
        
        al = 14 # length of an arrow in degrees
        c = a.get_perpendicular_between(b).normal()
        ca = c.return_angle_between(a)
        ang_sub = c.dir - a.dir
        # draw lines
        points=[]
        for angle in range (int(ca), int(ca + al), 5):
            if (ang_sub>0 and ang_sub<180) or \
               (ang_sub<-180 and ang_sub>-360):
                a = c.rotated(-1*angle)
            else:
                a = c.rotated(angle)
            if a.dip <0: a = Plane(a.dir+180, a.dip*-1)
            x,y = self.get_coords(a)
            points.append([x,y])
        for i in range(len(points)-1):
            cmds.append([   'line', 
                            [ points[i][0], points[i][1], 
                              points[i+1][0], points[i+1][1] ],
                            p_att[6]])
        # draw triangle
        x, y = points[-2]
        x1, y1 = points[-1]
        angl= atan2((y1-y),(x1-x)) + (radians(90))
        rv = .04 # length of a triangle lines
        xv1, yv1 = x1-sin(angl+radians(10))*rv, y1+cos(angl+radians(10))*rv
        xv2, yv2 = x1-sin(angl-radians(10))*rv, y1+cos(angl-radians(10))*rv
        #line xv1,yv1 - x1,y1
        cmds.append([   'line', 
                        [ xv1, yv1, 
                          x1, y1 ],
                        p_att[6]])
        #line xv2,yv2 - x1,y1
        cmds.append([   'line', 
                        [ xv2, yv2, 
                          x1, y1 ],
                        p_att[6]])
        
        ###### processing the smae 'w' for upper hemisphere
        a = Plane(p_att[0], p_att[1])
        if p_att[2]==0:p_att[2]=.0001
        if p_att[2]==180:p_att[2]=180.0001
        direction = p_att[2]
        if (a>90 and a<180) or (a<360 and a>270):
            direction = (direction+180)%360
        if (direction - a.dir)%180 < 90: direction = (direction + 180) % 360
        b = Plane(direction, .0001)
        
        al = 14 # length of an arrow in degrees
        c = a.get_perpendicular_between(b).normal()
        ca = c.return_angle_between(a)
        ang_sub = c.dir - a.dir
        # draw lines
        points=[]
        for angle in range (int(ca), int(ca + al), 5):
            if (ang_sub>0 and ang_sub<180) or \
               (ang_sub<-180 and ang_sub>-360):
                a = c.rotated(-1*angle)
            else:
                a = c.rotated(angle)
            if a.dip <0: a = Plane(a.dir+180, a.dip*-1)
            x,y = self.get_coords(a, 'upper')
            points.append([x,y])
        for i in range(len(points)-1):
            cmds.append([   'line', 
                            [ points[i][0], points[i][1], 
                              points[i+1][0], points[i+1][1] ],
                            p_att[6]])
        # draw triangle
        x, y = points[-2]
        x1, y1 = points[-1]
        angl= atan2((y1-y),(x1-x)) + (radians(90))
        rv = .04 # length of a triangle lines
        xv1, yv1 = x1-sin(angl+radians(10))*rv, y1+cos(angl+radians(10))*rv
        xv2, yv2 = x1-sin(angl-radians(10))*rv, y1+cos(angl-radians(10))*rv
        #line xv1,yv1 - x1,y1
        cmds.append([   'line', 
                        [ xv1, yv1, 
                          x1, y1 ],
                        p_att[6]])
        #line xv2,yv2 - x1,y1
        cmds.append([   'line', 
                        [ xv2, yv2, 
                          x1, y1 ],
                        p_att[6]])
        return cmds
            
    def t(self, p_att):
        print p_att
        a = Plane(p_att[0], p_att[1]).normal()
        if p_att[2]==0:p_att[2]=.0001
        if p_att[2]==180:p_att[2]=180.0001
        direction = p_att[2]
        if (a>90 and a<180) or (a<360 and a>270):
            direction = (direction+180)%360
        b = Plane(direction, .0001)
        cmds = []
        
        al = 14 # length of an arrow in degrees
        c = a.get_perpendicular_between(b).normal()
        ca = c.return_angle_between(a)
        ang_sub = c.dir - a.dir
        points=[]
        for angle in range (ca, ca + al, 5):
            if (ang_sub>0 and ang_sub<180) or \
               (ang_sub<-180 and ang_sub>-360):
                a = c.rotated(-1*angle)
            else:
                a = c.rotated(angle)
            if a.dip <0: a = Plane(a.dir+180, a.dip*-1)
            x,y = self.get_coords(a )
            points.append([x,y])
        for i in range(len(points)-1):
            cmds.append([   'line', 
                            [ points[i][0], points[i][1], 
                              points[i+1][0], points[i+1][1] ],
                            p_att[6]])
        b = Plane(p_att[2]+180, 0)
        a = Plane(p_att[0], p_att[1]).normal()
        c = a.get_perpendicular_between(b).normal()
        ca = c.return_angle_between(a)
        ang_sub = c.dir - a.dir
        points=[]
        for angle in range (ca, ca + al, 5):
            if (ang_sub>0 and ang_sub<180) or \
               (ang_sub<-180 and ang_sub>-360):
                a = c.rotated(-1*angle)
            else:
                a = c.rotated(angle)
            if a.dip <0: a = Plane(a.dir+180, a.dip*-1)
            x,y = self.get_coords(a )
            points.append([x,y])
        for i in range(len(points)-1):
            cmds.append([   'line', 
                            [ points[i][0], points[i][1], 
                              points[i+1][0], points[i+1][1] ],
                            p_att[6]])
        ### processing the same 't' for upper hemisphere
        a = Plane(p_att[0], p_att[1]).normal()
        if p_att[2]==0:p_att[2]=.0001
        if p_att[2]==180:p_att[2]=180.0001
        direction = p_att[2]
        if (a>90 and a<180) or (a<360 and a>270):
            direction = (direction+180)%360
        b = Plane(direction, .0001)
        
        al = 14 # length of an arrow in degrees
        c = a.get_perpendicular_between(b).normal()
        ca = c.return_angle_between(a)
        ang_sub = c.dir - a.dir
        points=[]
        for angle in range (ca, ca + al, 5):
            if (ang_sub>0 and ang_sub<180) or \
               (ang_sub<-180 and ang_sub>-360):
                a = c.rotated(-1*angle)
            else:
                a = c.rotated(angle)
            if a.dip <0: a = Plane(a.dir+180, a.dip*-1)
            x,y = self.get_coords(a, 'upper')
            points.append([x,y])
        for i in range(len(points)-1):
            cmds.append([   'line', 
                            [ points[i][0], points[i][1], 
                              points[i+1][0], points[i+1][1] ],
                            p_att[6]])
        b = Plane(p_att[2]+180, 0)
        a = Plane(p_att[0], p_att[1]).normal()
        c = a.get_perpendicular_between(b).normal()
        ca = c.return_angle_between(a)
        ang_sub = c.dir - a.dir
        points=[]
        for angle in range (ca, ca + al, 5):
            if (ang_sub>0 and ang_sub<180) or \
               (ang_sub<-180 and ang_sub>-360):
                a = c.rotated(-1*angle)
            else:
                a = c.rotated(angle)
            if a.dip <0: a = Plane(a.dir+180, a.dip*-1)
            x,y = self.get_coords(a, 'upper')
            points.append([x,y])
        for i in range(len(points)-1):
            cmds.append([   'line', 
                            [ points[i][0], points[i][1], 
                              points[i+1][0], points[i+1][1] ],
                            p_att[6]])
        return cmds

    def r(self, p_att):
        a = Plane(p_att[0], p_att[1]).normal()
        if p_att[2]==0:p_att[2]=.0001
        if p_att[2]==180:p_att[2]=180.0001
        direction = p_att[2]
        if (a>90 and a<180) or (a<360 and a>270):
            direction = (direction+180)%360
        if (direction - a.dir)%180 < 90: direction = (direction + 180) % 360
        b = Plane(direction, .0001)
        cmds = []
        
        al = 14 # length of an arrow in degrees
        c = a.get_perpendicular_between(b).normal()
        ca = c.return_angle_between(a)
        ang_sub = c.dir - a.dir
        # draw lines
        points=[]
        for angle in range (ca, ca + al, 5):
            if (ang_sub>0 and ang_sub<180) or \
               (ang_sub<-180 and ang_sub>-360):
                a = c.rotated(-1*angle)
            else:
                a = c.rotated(angle)
            if a.dip <0: a = Plane(a.dir+180, a.dip*-1)
            x,y = self.get_coords(a)
            points.append([x,y])
        for i in range(len(points)-1):
            cmds.append([   'line', 
                            [ points[i][0], points[i][1], 
                              points[i+1][0], points[i+1][1] ],
                            p_att[6]])
        # draw triangle
        x, y = points[-2]
        x1, y1 = points[-1]
        angl= atan2((y1-y),(x1-x)) + (radians(90))
        rv = .04 # length of a triangle lines
        xv1, yv1 = x1-sin(angl+radians(10))*rv, y1+cos(angl+radians(10))*rv
        xv2, yv2 = x1-sin(angl-radians(10))*rv, y1+cos(angl-radians(10))*rv
        #line xv1,yv1 - x1,y1
        cmds.append([   'line', 
                        [ xv1, yv1, 
                          x1, y1 ],
                        p_att[6]])
        #line xv2,yv2 - x1,y1
        cmds.append([   'line', 
                        [ xv2, yv2, 
                          x1, y1 ],
                        p_att[6]])
        ### processing the smae 'r' for upper hemisphere
        a = Plane(p_att[0], p_att[1]).normal()
        if p_att[2]==0:p_att[2]=.0001
        if p_att[2]==180:p_att[2]=180.0001
        direction = p_att[2]
        if (a>90 and a<180) or (a<360 and a>270):
            direction = (direction+180)%360
        if (direction - a.dir)%180 < 90: direction = (direction + 180) % 360
        b = Plane(direction, .0001)
        
        al = 14 # length of an arrow in degrees
        c = a.get_perpendicular_between(b).normal()
        ca = c.return_angle_between(a)
        ang_sub = c.dir - a.dir
        # draw lines
        points=[]
        for angle in range (ca, ca + al, 5):
            if (ang_sub>0 and ang_sub<180) or \
               (ang_sub<-180 and ang_sub>-360):
                a = c.rotated(-1*angle)
            else:
                a = c.rotated(angle)
            if a.dip <0: a = Plane(a.dir+180, a.dip*-1)
            x,y = self.get_coords(a, 'upper')
            points.append([x,y])
        for i in range(len(points)-1):
            cmds.append([   'line', 
                            [ points[i][0], points[i][1], 
                              points[i+1][0], points[i+1][1] ],
                            p_att[6]])
        # draw triangle
        x, y = points[-2]
        x1, y1 = points[-1]
        angl= atan2((y1-y),(x1-x)) + (radians(90))
        rv = .04 # length of a triangle lines
        xv1, yv1 = x1-sin(angl+radians(10))*rv, y1+cos(angl+radians(10))*rv
        xv2, yv2 = x1-sin(angl-radians(10))*rv, y1+cos(angl-radians(10))*rv
        #line xv1,yv1 - x1,y1
        cmds.append([   'line', 
                        [ xv1, yv1, 
                          x1, y1 ],
                        p_att[6]])
        #line xv2,yv2 - x1,y1
        cmds.append([   'line', 
                        [ xv2, yv2, 
                          x1, y1 ],
                        p_att[6]])
        
        return cmds

    def get_coords(self, pln, hemisphere = 'lower'):
        if (hemisphere == 'lower' and pln.cos2 < 0) or \
                                (hemisphere == 'upper' and pln.cos2 > 0) :
            return (pln.dir*2/360.0-1),  (pln.dip/90.0-1)
        else:
            return ((((pln.dir*2/360.0)+1)%2)-1), (1-pln.dip/90.0)
